// Boost Thread implementation
// Copyright 2005 (C) Ralph Thomas

#include <vector>
#include <util/thread.h>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/xtime.hpp>
#include <boost/bind.hpp>

namespace util {
	class threadImpl {
		typedef std::vector<thread::work*> workList;
		typedef boost::mutex::scoped_lock lock;

		workList _work;			///< Work to perform.
		boost::mutex _workMutex;	///< Lock for _work.
		boost::condition _startWorkCond;///< Signaled on work add.
		bool _exitThread;		///< Should we exit?
		boost::mutex _exitThreadMutex;	///< Lock for _exitThread.
		boost::thread* _workThread;	///< Thread which does work.
		boost::mutex _threadMutex;	///< Mutex guarding _workThread.
		//
		// work* getNextWorkItem()
		//
		/// Return the next item of work from the queue, removing
		/// it. The function locks and then unlocks _workMutex.
		///
		/// \return	the next item of work to perform, or zero
		///		if there is no more work.
		//
		thread::work* getNextWorkItem() {
			lock lk(_workMutex);
			thread::work* myWork = 0;
			//
			// First try and find the earliest item of work with
			// user priority.
			//
			workList::iterator i = _work.begin();
			while( i != _work.end() ) {
				if( !(*i) ) continue;
				if( (*i)->getPriority() ==
					thread::work::kWorkPriorityUser ) break;
				i++;
			}
			if( i == _work.end() ) {
				//
				// There were no items in the list of work with
				// user priority. Look for one with regular
				// priority.
				//
				if( _work.size() ) i = _work.begin();
			}
			//
			// Take the pointer to the work item, and remove this
			// 
			if( i != _work.end() ) {
				myWork = *i;
				_work.erase( i );
			}
			//
			// We are now done with the _work vector.
			//
			return myWork;
		}
	  public:
		//
		// void run()
		//
		/// This is the Boost.Thread entrypoint into our work. This
		/// function waits for work to be added (signaled by
		/// _startWorkCond). This function is where most of the
		/// thread implementation lives.
		//
		void run() {
			while( true ) {
				//
				// Get the next item of work to run.
				//
				thread::work* w = getNextWorkItem();
				//
				// If we were able to get any work then
				// we can perform it, otherwise we should
				// just wait for more work to be added.
				//
				if( w ) {
					//
					// Perform the work, then delete
					// it.
					//
					w->doWork();
					delete w;
				} else {
					//
					// Wait for work to be added to
					// the work queue.
					//
					lock lk(_workMutex);
					//
					// This timeout is a last ditch thing
					// to make sure that we aren't supposed
					// to quit. It's possible that we could
					// miss the "pre-join" signal being
					// fired and get stuck here forever.
					//
					boost::xtime timeout;
					boost::xtime_get( &timeout, boost::TIME_UTC );
					//
					// Wait for a second before timing out.
					//
					timeout.sec++;
					_startWorkCond.timed_wait(lk, timeout);
				}
				//
				// Before we loop, we should check to see
				// if it's time to exit. We have to do
				// this in a scope to make sure we don't
				// double lock _workMutex.
				//
				{
					lock lk(_exitThreadMutex);
					if( _exitThread ) return;
				}
				//
				// Loop around and look for work again.
				//
			}
			//
			// Not reached.
			//
		}
		threadImpl() : _exitThread( false ), _workThread( 0 ) {}
		~threadImpl() {
			//
			// Bring down the thread gracefully. By setting
			// _exitThread we hope that when the thread
			// checks after doing some work.
			//
			lock lk( _exitThreadMutex );
			_exitThread = true;
			lk.unlock();
			//
			// There's a chance that the thread is stuck
			// waiting for more work. Wake it up so that
			// it can exit.
			//
			_startWorkCond.notify_all();
			//
			// Join onto the thread and return once it's
			// quit.
			//
			lock lk2( _threadMutex );
			if( _workThread ) {
				_workThread->join();
				delete _workThread;
			}
			lk2.unlock();
		}
		void adoptWork( thread::work* theWork ) {
			lock lk( _workMutex );
			_work.push_back( theWork );
			lk.unlock();
			//
			// Wake up the worker thread.
			//
			_startWorkCond.notify_all();
			//
			// Make sure we actually have a worker thread
			// to do this work.
			//
			lock lk2( _threadMutex );
			if( !_workThread ) _workThread = new boost::thread( boost::bind( &threadImpl::run, this ) );
			lk2.unlock();
		}
	};

	thread::thread() : _impl( new threadImpl ) {}
	thread::~thread() { delete _impl; }
	void thread::adoptWork( work* theWork ) { _impl->adoptWork( theWork ); }
	void thread::adoptWork( const boost::function<void()>& fn ) {
		_impl->adoptWork( new boostWork( fn ) );
	}
	thread::work::~work() {}
	thread::boostWork::boostWork( const boost::function<void ()>& fn ) {
		_work = fn;
	}
	thread::boostWork::~boostWork() {}
	void thread::boostWork::doWork() { _work(); }
	thread::work::priority thread::boostWork::getPriority() const {
		return thread::work::kWorkPriorityRegular;
	}
}

